/**
 * Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
 *
 *         Project home : http://code.daikit.com/daikit4gxt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.daikit.daikit4gxt.client.ui.editor.field;

import com.daikit.commons.shared.utils.DkObjectUtils;
import com.daikit.daikit4gxt.client.action.DkQueryPagingLoadConfig;
import com.daikit.daikit4gxt.client.labelprovider.DkAsSimpleStringLabelProvider;
import com.daikit.daikit4gxt.client.ui.edit.BeforeOpenEvent;
import com.daikit.daikit4gxt.client.ui.edit.BeforeOpenEvent.BeforeOpenHandler;
import com.daikit.daikit4gxt.client.ui.edit.BeforeOpenEvent.HasBeforeOpenHandlers;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.sencha.gxt.cell.core.client.form.ComboBoxCell;
import com.sencha.gxt.core.client.IdentityValueProvider;
import com.sencha.gxt.core.client.XTemplates;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.data.shared.LabelProvider;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.widget.core.client.ListView;


/**
 * Cell for {@link DkComboBox}
 *
 * @author tcaselli
 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
 * @param <B>
 */
public class DkComboboxCell<B> extends ComboBoxCell<B> implements HasBeforeOpenHandlers<B>
{

	private ItemLocaleQueryMatcher<B> itemQueryMatcher = null;

	/**
	 * An {@link XTemplates} permitting to apply style to rendered items.
	 *
	 * @param <B>
	 *
	 */
	public abstract static interface MyDynamicComboboxXTemplate<B> extends XTemplates
	{
		/**
		 * Should be overridden and annotated with: <br>
		 * "@XTemplate("&lt;div class='style.method_in_CssResource'&gt; ... &lt;/div&gt;")"
		 *
		 * @param item
		 * @param cssResource
		 * @return {@link SafeHtml}
		 */
		abstract SafeHtml render(final B item, final CssResource cssResource);
	}

	/**
	 * A clientBundle permitting to retrieve a CSS file to be used to render MyDynamicComboboxXTemplate<br>
	 * to instantiate it :<br>
	 * <code>
	 * X extends MyDynamicComboboxCssBundle<br>
	 * final X b = GWT.create(X.class);<br>
	 * b.css().ensureInjected();
	 * </code>
	 *
	 * @param <CSS_RESOURCE>
	 *           the type of the {@link CssResource} to be used to render this template
	 *
	 */
	@SuppressWarnings("javadoc")
	public abstract interface MyDynamicComboboxCssBundle<CSS_RESOURCE extends CssResource> extends ClientBundle
	{
		/**
		 * Should be overridden and annotated with:<br>
		 * "@Source("file_path.css")"
		 *
		 * @return a type extending {@link CssResource}
		 */
		abstract CSS_RESOURCE css();
	}

	/**
	 * A Matcher for filtering a local combobox
	 *
	 * @author tcaselli
	 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
	 * @param <B>
	 */
	public interface ItemLocaleQueryMatcher<B>
	{
		/**
		 * Default is : if (value != null) return renderedItem.toLowerCase().startsWith(query.toLowerCase());
		 *
		 * @param item
		 *           the item
		 * @param query
		 *           the query
		 * @param renderedItem
		 *           the item rendered as String
		 * @return whether the given item matches the given query
		 */
		boolean matches(B item, String query, String renderedItem);
	}

	/**
	 * Wrapper to {@link #DkComboboxCell(ListStore, LabelProvider)} with a {@link DkAsSimpleStringLabelProvider}
	 *
	 * @param store
	 *           the store
	 */
	public DkComboboxCell(final ListStore<B> store)
	{
		this(store, new DkAsSimpleStringLabelProvider<B>());
	}

	/**
	 * Constructor
	 *
	 * @param store
	 * @param labelProvider
	 */
	public DkComboboxCell(final ListStore<B> store, final LabelProvider<? super B> labelProvider)
	{
		super(store, labelProvider, new ListView<B, B>(store, new IdentityValueProvider<B>(), new AbstractCell<B>()
		{
			@Override
			public void render(final com.google.gwt.cell.client.Cell.Context context, final B value, final SafeHtmlBuilder sb)
			{
				sb.appendHtmlConstant(labelProvider.getLabel(value));
			}
		}));
		initialize();
	}

	/**
	 *
	 * @param store
	 * @param labelProvider
	 * @param choicesTemplate
	 * @param cssBundle
	 */
	public DkComboboxCell(final ListStore<B> store, final LabelProvider<? super B> labelProvider, final MyDynamicComboboxXTemplate<B> choicesTemplate,
			final MyDynamicComboboxCssBundle<?> cssBundle)
	{
		super(store, labelProvider, new ListView<B, B>(store, new IdentityValueProvider<B>(), new AbstractCell<B>()
		{
			@Override
			public void render(final com.google.gwt.cell.client.Cell.Context context, final B value, final SafeHtmlBuilder sb)
			{
				sb.append(choicesTemplate.render(value, cssBundle.css()));
			}
		}));
		initialize();
	}


	protected void initialize()
	{
		//		set
	}

	private boolean isTriggerClick = false;

	@Override
	protected void onTriggerClick(final com.google.gwt.cell.client.Cell.Context context, final XElement parent, final NativeEvent event, final B value,
			final ValueUpdater<B> updater)
	{
		if (!isExpanded())
		{
			isTriggerClick = true;
		}
		fireEvent(new BeforeOpenEvent<B>(getStore()));
		super.onTriggerClick(context, parent, event, value, updater);
	}

	@Override
	public void doQuery(final com.google.gwt.cell.client.Cell.Context context, final XElement parent, final ValueUpdater<B> updater, final B value,
			final String query, final boolean force)
	{
		if (parent == null)
		{
			isTriggerClick = false;
			lastQuery = query;
		}
		else
		{
			final String sentQuery;//!DkObjectUtils.equalsWithNull(lastQuery, query) &&
			if (isExpanded() || !isTriggerClick) // to not use query in the case when the user clicks on the drop down button or when the user is in the editor and changed one letter on something already there
			{
				sentQuery = query;
			}
			else
			{
				sentQuery = "";
			}
			isTriggerClick = false;
			if (!isExpanded() && getMode() == QueryMode.REMOTE)
			{
				// in this case a search will be triggered and the previous drop down content will be changed
				// so let's clear the store before the drop down is displayed.
				getListView().getStore().clear();
			}
			if (DkObjectUtils.equalsWithNull(lastQuery, sentQuery))
			{
				lastQuery = sentQuery == null ? "" : null; // to trigger a search all the time , because useQueryCache boolean is not accessible
			}
			super.doQuery(context, parent, updater, value, sentQuery, force);
			lastQuery = query;
		}
	}

	@Override
	protected PagingLoadConfig getParams(final String query)
	{
		final DkQueryPagingLoadConfig config = new DkQueryPagingLoadConfig();
		config.setLimit(pageSize);
		config.setOffset(0);
		config.setQuery(query);
		return config;
	}


	@Override
	protected boolean itemMatchesQuery(final B item, final String query)
	{
		return itemQueryMatcher == null ? super.itemMatchesQuery(item, query) : itemQueryMatcher.matches(item, query, getPropertyEditor().render(item));
	}

	/**
	 * @param itemQueryMatcher
	 *           the itemQueryMatcher to set
	 */
	public void setItemLocalQueryMatcher(final ItemLocaleQueryMatcher<B> itemQueryMatcher)
	{
		this.itemQueryMatcher = itemQueryMatcher;
	}

	@Override
	public HandlerRegistration addBeforeOpenHandler(final BeforeOpenHandler<B> handler)
	{
		return addHandler(handler, BeforeOpenEvent.getType());
	}

}
